const { useState } = React

// 设置用的本地储存key
const SETTING_STORAGE_KEY = {
  SHOW_COLOR: 'show-color',
  MAJOR_OPTION: 'major-option',
}

// 颜色
const COLOR_LIST = [
  '#c11f44',
  '#ee373a',
  '#f15a39',
  '#f78b37',
  '#fec728',
  '#f5f031',
  '#9ece4f',
  '#77c358',
  '#268e8e',
  '#4e64b0',
  '#5d54a4',
  '#8a3e8b',
]

// 十二平均律/音名数组 // b-flat-降调；#-sharp-升调；
const PITCH_NAME_LIST = [
  { name: 'C' },
  { name: '#C' },
  { name: 'D' },
  { name: '#D' },
  { name: 'E' },
  { name: 'F' },
  { name: '#F' },
  { name: 'G' },
  { name: '#G' },
  { name: 'A' },
  { name: '#A' },
  { name: 'B' },
  { name: 'C' },
].map((item, i) => ({ ...item, color: COLOR_LIST[i % 12] }))

// 大调唱名数组
const MAJOR_SYLLABLE_NAME_LIST = [
  { name: 'do', sort: 1 },
  {},
  { name: 're', sort: 2 },
  {},
  { name: 'mi', sort: 3 },
  { name: 'fa', sort: 4 },
  {},
  { name: 'sol', sort: 5 },
  {},
  { name: 'la', sort: 6 },
  {},
  { name: 'si', sort: 7 },
  { name: 'do', sort: 8 },
]

// 频率最低点、四弦的空弦、初始第五位的半音："E"
const DEFAULT_TONE = 4

// 每根弦之间相隔的半音数
const STRING_TONE_SPACE = 5

// 品数 + 空弦
const PIN_COUNT = 25

// 四根弦及空弦半音位置
const STRINGS = new Array(4).fill(null).map((_, index) => ({
  defaultTone: DEFAULT_TONE + index * STRING_TONE_SPACE,
}))

const SINGLE_STRING = new Array(PIN_COUNT).fill(null) // 由半音组成的单根弦

// 公共行内样式
const BORDER_STYLE = '2px solid #000000'
const EMPTY_BORDER_STYLE = '2px solid rgba(0, 0, 0, 0)'

// component: 一根竖线
const VLine = ({ long = 50 }) => {
  return <div style={{ height: `${long}px`, borderLeft: BORDER_STYLE }}></div>
}

// component: 弦上的单个品格
const StepBox = ({ leftLine = true, bottomLine = true, height = 150, width = 100, text = '' }) => {
  return (
    <div
      className='step-box-wrap'
      style={{
        height,
        width,
        borderLeft: leftLine ? BORDER_STYLE : EMPTY_BORDER_STYLE,
        borderBottom: bottomLine ? BORDER_STYLE : EMPTY_BORDER_STYLE,
      }}
    >
      {text && <div className='step-box-text'>{text}</div>}
    </div>
  )
}

const App = () => {
  // 是否显示颜色
  const [showColor, setShowColor] = useState(
    Boolean(window.localStorage.getItem(SETTING_STORAGE_KEY.SHOW_COLOR) === 'true')
  )
  // 自然大调的调性（相对位置）
  const [majorPosition, setMajorPosition] = useState(
    Number(window.localStorage.getItem(SETTING_STORAGE_KEY.MAJOR_OPTION)) ?? 0
  )

  // 是否显示颜色
  const onChangeShowColor = (e) => {
    setShowColor((pre) => {
      const res = !pre
      window.localStorage.setItem(SETTING_STORAGE_KEY.SHOW_COLOR, res)
      return res
    })
  }

  // 更换大调调性
  const onChangeMajorTonality = (index) => {
    setMajorPosition(index)
    window.localStorage.setItem(SETTING_STORAGE_KEY.MAJOR_OPTION, index)
  }

  // 根据调性决定唱名列表
  const singList = PITCH_NAME_LIST.map(
    (item, index) => PITCH_NAME_LIST[(index + majorPosition) % 12]
  )

  return (
    <div className='page-wrap flex-row-space-around'>
      <div className='page-left'>
        {/* 音调关系 */}
        <div style={{ display: 'flex', borderTop: BORDER_STYLE, borderBottom: BORDER_STYLE }}>
          {singList.map((item, index) => (
            <div key={index} className='page-tone-item flex-column-middle'>
              <VLine />
              <div
                className='flex-column-middle'
                style={{
                  width: '100%',
                  ...(showColor &&
                    MAJOR_SYLLABLE_NAME_LIST[index]?.sort && { backgroundColor: item.color }),
                }}
              >
                <div className='font-size-big'>{index + 1}</div>
                <div className='font-size-big'>-</div>
                <div className='font-size-big font-weight-bold'>
                  {MAJOR_SYLLABLE_NAME_LIST[index]?.sort || '-'}
                </div>
                <div className='font-size-big font-weight-bold'>{item.name}</div>
                <div className='font-size-big font-weight-bold'>
                  {MAJOR_SYLLABLE_NAME_LIST[index]?.name || '-'}
                </div>
              </div>
              <VLine />
            </div>
          ))}
        </div>
        {/* 设置 */}
        <div style={{ width: 150 }}>
          <div onClick={onChangeShowColor}>
            <label>show color: </label>
            <input type='checkbox' defaultChecked={showColor} />
          </div>
          <div>tonality (major): </div>
          <div style={{ marginLeft: 20 }}>
            {PITCH_NAME_LIST.map((item, index) => (
              <div
                key={index}
                onClick={() => onChangeMajorTonality(index)}
                style={{
                  ...(majorPosition === index && {
                    backgroundColor: PITCH_NAME_LIST[index]?.color,
                  }),
                }}
              >
                {item.name}
              </div>
            ))}
          </div>
        </div>
      </div>
      {/* 贝斯弦 */}
      <div className='page-right flex-row-space-around' style={{ justifyContent: 'flex-start' }}>
        {STRINGS.map((item, stringIndex) => (
          <div key={stringIndex}>
            {SINGLE_STRING.map((_, pinIndex) => {
              const stepIndex = item.defaultTone + pinIndex // 品格对应第几个半音
              const tone = PITCH_NAME_LIST[stepIndex % 12] // 找到对应的半音对象
              const layer = Math.floor(stepIndex / 12) // 第几层
              const syllable = MAJOR_SYLLABLE_NAME_LIST[(stepIndex + 12 - majorPosition) % 12] // 找对应唱名
              // const bgOpacity = (255 - (layer - 1) * 60).toString(16) // 层数越高，越透明
              const bgOpacity = 'ff'
              return (
                <div key={`${stringIndex}-${pinIndex}`} className='pin-step-wrap'>
                  <StepBox
                    text={
                      <div
                        className='flex-column-middle'
                        style={{
                          width: 70,
                          ...(showColor &&
                            syllable?.name && { backgroundColor: `${tone.color}${bgOpacity}` }),
                        }}
                      >
                        <div>
                          <span className='font-weight-bold'>{tone.name}</span>
                          <span className='font-size-small'>({layer})</span>
                        </div>
                        <div className='font-weight-bold'>
                          {syllable?.name ? `${syllable?.name}/${syllable?.sort}` : '-'}
                        </div>
                        <div>{stepIndex - DEFAULT_TONE + 1}</div>
                      </div>
                    }
                    leftLine={pinIndex !== 0} // 空弦去掉 borderLeft
                    bottomLine={stringIndex !== STRINGS.length - 1} // 1弦去掉 borderBottom
                  />
                  {/* 品格标识 */}
                  {stringIndex === STRINGS.length - 1 &&
                    [3, 5, 7, 9, 15, 17, 19, 21].indexOf(pinIndex) !== -1 && (
                      <div className='pin-step-mark font-weight-bold'>{`* ${pinIndex}`}</div>
                    )}
                  {stringIndex === STRINGS.length - 1 && [12, 24].indexOf(pinIndex) !== -1 && (
                    <div className='pin-step-mark font-weight-bold'>{`** ${pinIndex}`}</div>
                  )}
                </div>
              )
            })}
          </div>
        ))}
      </div>
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))
